from django.conf import settings
import requests
import logging
from typing import Dict, List

logger = logging.getLogger('apps')


class OCRServiceError(Exception):
    """OCR服务异常"""

    def __init__(self, message, code=None, http_status=500):
        self.message = message
        self.code = code
        self.http_status = http_status
        super().__init__(message)

class TableOCRService:
    """Textin 通用表格识别的 API"""

    def __init__(self):
        self.config = settings.AI_CONFIG.get('ocr_service', {})
        self.app_id = self.config.get('app_id', '1a4137efec74924d48c7f79d130340f9')
        self.secret_code = self.config.get('secret_code', 'a1b14d43c208cba6f18f856784f7ed71')
        self.api_url = 'https://api.textin.com/ai/service/v2/recognize/table/multipage'
        self.max_retries = self.config.get('max_retries', 3)
        self.timeout = self.config.get('timeout', 60)  # 表格处理可能需要更长时间

    def recognize_table(self, image_file, options: Dict = None) -> Dict:
        """
        识别图片中的表格

        Args:
            image_file: 图片文件对象
            options: 识别选项，可包含以下参数：
                - character: 是否返回完整的字符信息，0或1，默认0
                - straighten: 坐标系选项，0或1，默认0
                - output_order: 输出格式，可选值: perpendicular, table_and_remain, table_only
                - table_type_hint: 表格类型提示，可选值: automatic, table_with_line, table_with_few_line, table_without_line
                - excel: 是否输出excel文件，0或1，默认0

        Returns:
            Dict: 识别结果
        """
        try:
            # 验证图片文件
            if not self.validate_image(image_file):
                raise OCRServiceError("图片文件验证失败，请检查文件格式和大小", code="INVALID_IMAGE")

            # 准备请求头
            headers = {
                'x-ti-app-id': self.app_id,
                'x-ti-secret-code': self.secret_code,
                'Content-Type': 'application/octet-stream'
            }

            # 准备URL参数
            params = {}
            
            # 添加可选参数
            if options:
                if 'character' in options:
                    params['character'] = options.get('character')
                if 'straighten' in options:
                    params['straighten'] = options.get('straighten')
                if 'output_order' in options and options['output_order'] in ['perpendicular', 'table_and_remain', 'table_only']:
                    params['output_order'] = options.get('output_order')
                if 'table_type_hint' in options and options['table_type_hint'] in ['automatic', 'table_with_line', 'table_with_few_line', 'table_without_line']:
                    params['table_type_hint'] = options.get('table_type_hint')
                if 'excel' in options:
                    params['excel'] = options.get('excel')

            logger.info(f"表格识别请求参数: {params}")
            
            # 读取图片内容
            image_content = image_file.read()

            # 发送请求，带重试机制
            response = None
            for retry in range(self.max_retries):
                try:
                    logger.info(f"发送表格识别请求到 {self.api_url}，第{retry+1}次尝试")
                    response = requests.post(
                        self.api_url,
                        headers=headers,
                        params=params,
                        data=image_content,
                        timeout=self.timeout
                    )

                    # 检查响应状态
                    if response.status_code == 200:
                        logger.info(f"表格识别API请求成功，状态码: {response.status_code}")
                        break

                    # 如果是服务器错误，重试
                    if response.status_code >= 500:
                        logger.warning(
                            f"表格识别API服务器错误，重试 {retry + 1}/{self.max_retries}: {response.status_code}")
                        continue

                    # 其他错误，直接抛出
                    logger.error(f"表格识别API请求失败: {response.status_code}, {response.text}")
                    raise OCRServiceError(f"表格识别API请求失败: {response.status_code}",
                                        code=f"HTTP_{response.status_code}")

                except requests.RequestException as e:
                    logger.warning(f"表格识别API请求异常，重试 {retry + 1}/{self.max_retries}: {str(e)}")
                    if retry == self.max_retries - 1:
                        raise OCRServiceError(f"表格识别API请求异常: {str(e)}", code="REQUEST_ERROR")

            if not response:
                raise OCRServiceError("表格识别API请求失败，无响应", code="NO_RESPONSE")

            # 解析响应
            result = response.json()
            logger.debug(f"表格识别API原始响应: {result}")

            # 检查响应码
            if result.get('code', 0) != 0 and result.get('code', 0) != 200:
                logger.error(f"表格识别失败: {result.get('message')}")
                raise OCRServiceError(f"表格识别失败: {result.get('message', '未知错误')}",
                                    code=f"API_{result.get('code')}")

            # 记录识别结果
            page_count = len(result.get('result', {}).get('pages', []))
            logger.info(f"表格识别成功，共处理 {page_count} 页")
            
            # 记录表格数量
            tables_count = 0
            for page in result.get('result', {}).get('pages', []):
                tables_count += len(page.get('tables', []))
            
            logger.info(f"表格识别结果: 共有{tables_count}个表格")

            return result

        except OCRServiceError:
            # 直接抛出已经格式化的错误
            raise
        except Exception as e:
            logger.error(f"表格识别失败: {str(e)}", exc_info=True)
            raise OCRServiceError(f"表格识别失败: {str(e)}", code="UNKNOWN_ERROR")

    def recognize_table_from_url(self, image_url: str, options: Dict = None) -> Dict:
        """
        从URL识别图片中的表格

        Args:
            image_url: 图片文件URL
            options: 识别选项，同recognize_table

        Returns:
            Dict: 识别结果
        """
        try:
            # 验证URL
            if not image_url or not (image_url.startswith('http://') or image_url.startswith('https://')):
                raise OCRServiceError("无效的图片URL", code="INVALID_URL")

            # 准备请求头
            headers = {
                'x-ti-app-id': self.app_id,
                'x-ti-secret-code': self.secret_code,
                'Content-Type': 'text/plain'
            }

            # 准备URL参数
            params = {}
            
            # 添加可选参数
            if options:
                if 'character' in options:
                    params['character'] = options.get('character')
                if 'straighten' in options:
                    params['straighten'] = options.get('straighten')
                if 'output_order' in options and options['output_order'] in ['perpendicular', 'table_and_remain', 'table_only']:
                    params['output_order'] = options.get('output_order')
                if 'table_type_hint' in options and options['table_type_hint'] in ['automatic', 'table_with_line', 'table_with_few_line', 'table_without_line']:
                    params['table_type_hint'] = options.get('table_type_hint')
                if 'excel' in options:
                    params['excel'] = options.get('excel')

            logger.info(f"URL表格识别请求参数: {params}, 图片URL: {image_url[:50]}...")
            
            # 发送请求，带重试机制
            response = None
            for retry in range(self.max_retries):
                try:
                    logger.info(f"发送URL表格识别请求到 {self.api_url}，第{retry+1}次尝试")
                    response = requests.post(
                        self.api_url,
                        headers=headers,
                        params=params,
                        data=image_url,
                        timeout=self.timeout
                    )

                    # 检查响应状态
                    if response.status_code == 200:
                        logger.info(f"URL表格识别API请求成功，状态码: {response.status_code}")
                        break

                    # 如果是服务器错误，重试
                    if response.status_code >= 500:
                        logger.warning(
                            f"表格URL识别API服务器错误，重试 {retry + 1}/{self.max_retries}: {response.status_code}")
                        continue

                    # 其他错误，直接抛出
                    logger.error(f"表格URL识别API请求失败: {response.status_code}, {response.text}")
                    raise OCRServiceError(f"表格URL识别API请求失败: {response.status_code}",
                                        code=f"HTTP_{response.status_code}")

                except requests.RequestException as e:
                    logger.warning(f"表格URL识别API请求异常，重试 {retry + 1}/{self.max_retries}: {str(e)}")
                    if retry == self.max_retries - 1:
                        raise OCRServiceError(f"表格URL识别API请求异常: {str(e)}", code="REQUEST_ERROR")

            if not response:
                raise OCRServiceError("表格URL识别API请求失败，无响应", code="NO_RESPONSE")

            # 解析响应
            result = response.json()
            logger.debug(f"URL表格识别API原始响应: {result}")

            # 检查响应码
            if result.get('code', 0) != 0 and result.get('code', 0) != 200:
                logger.error(f"表格URL识别失败: {result.get('message')}")
                raise OCRServiceError(f"表格URL识别失败: {result.get('message', '未知错误')}",
                                    code=f"API_{result.get('code')}")

            # 记录识别结果
            page_count = len(result.get('result', {}).get('pages', []))
            logger.info(f"URL表格识别成功，共处理 {page_count} 页")
            
            # 记录表格数量
            tables_count = 0
            for page in result.get('result', {}).get('pages', []):
                tables_count += len(page.get('tables', []))
            
            logger.info(f"URL表格识别结果: 共有{tables_count}个表格")

            return result

        except OCRServiceError:
            # 直接抛出已经格式化的错误
            raise
        except Exception as e:
            logger.error(f"表格URL识别失败: {str(e)}", exc_info=True)
            raise OCRServiceError(f"表格URL识别失败: {str(e)}", code="UNKNOWN_ERROR")

    def validate_image(self, image_file) -> bool:
        """
        验证图片文件

        Args:
            image_file: 图片文件对象

        Returns:
            bool: 是否验证通过
        """
        try:
            # 检查文件大小
            max_size = self.config.get('max_file_size', 10 * 1024 * 1024)  # 默认10MB
            if image_file.size > max_size:
                logger.warning(f"图片文件大小超过限制: {image_file.size} > {max_size}")
                return False

            # 检查文件格式
            allowed_formats = ['jpg', 'jpeg', 'png', 'bmp', 'pdf', 'tiff', 'gif']
            file_ext = image_file.name.split('.')[-1].lower()
            if file_ext not in allowed_formats:
                logger.warning(f"文件格式不支持: {file_ext}, 支持的格式: {allowed_formats}")
                return False

            return True

        except Exception as e:
            logger.error(f"图片文件验证失败: {str(e)}")
            return False

    def extract_tables_to_markdown(self, result: Dict) -> str:
        """
        将表格识别结果转换为Markdown格式

        Args:
            result: 表格识别结果

        Returns:
            str: Markdown格式的表格
        """
        try:
            # 日志记录原始结果，方便调试
            logger.debug(f"开始解析表格结果: {result}")
            
            # 检查API结果格式
            if 'result' not in result or 'pages' not in result['result']:
                logger.warning("表格识别结果格式不正确: 缺少result或pages字段")
                return "无法识别表格内容，API返回格式不正确"
            
            # 检查返回的页面是否为空
            pages = result['result']['pages']
            if not pages:
                logger.warning("表格识别结果不包含任何页面")
                return "未检测到任何内容"
            
            markdown_tables = []
            table_count = 0
            line_count = 0
            
            # 处理每一页
            for page_index, page in enumerate(pages):
                logger.info(f"处理第{page_index+1}页, 页面数据: {page.keys()}")
                
                # 处理表格区域
                if 'tables' in page and page['tables']:
                    for table_index, table in enumerate(page['tables']):
                        logger.info(f"处理表格 {table_index+1}, 表格数据: {table.keys()}")
                        table_count += 1
                        
                        # 准备表格标题
                        table_title = f"## 第{page_index+1}页 表格{table_index+1}\n\n"
                        
                        # 检查表格类型
                        if table.get('type') == 'plain':
                            # 纯文本表格，检查是否有行文本
                            if 'lines' in table and table['lines']:
                                text_lines = []
                                for line in table['lines']:
                                    if line.get('text', '').strip():
                                        text_lines.append(line.get('text', ''))
                                        line_count += 1
                                
                                text_content = "\n".join(text_lines)
                                if text_content.strip():
                                    markdown_tables.append(f"{table_title}```\n{text_content}\n```\n\n")
                                else:
                                    markdown_tables.append(f"{table_title}（表格无有效文本内容）\n\n")
                            else:
                                markdown_tables.append(f"{table_title}（表格无有效内容）\n\n")
                            continue
                        
                        # 处理包含table_cells的表格
                        if 'table_cells' in table and table['table_cells']:
                            try:
                                # 获取表格的行数和列数
                                rows_count = table.get('table_rows', 0)
                                cols_count = table.get('table_cols', 0)
                                
                                logger.info(f"表格维度: {rows_count}行 x {cols_count}列")
                                
                                if rows_count <= 0 or cols_count <= 0:
                                    markdown_tables.append(f"{table_title}（表格结构无效）\n\n")
                                    continue
                                
                                # 创建空表格
                                grid = [['' for _ in range(cols_count)] for _ in range(rows_count)]
                                
                                # 填充表格内容
                                for cell in table['table_cells']:
                                    start_row = cell.get('start_row', 0)
                                    end_row = cell.get('end_row', 0)
                                    start_col = cell.get('start_col', 0)
                                    end_col = cell.get('end_col', 0)
                                    text = cell.get('text', '').replace('|', '\\|')  # 转义管道符
                                    
                                    # 处理单元格跨行跨列
                                    for r in range(start_row, end_row + 1):
                                        for c in range(start_col, end_col + 1):
                                            if r < rows_count and c < cols_count:
                                                # 对于跨行跨列的单元格，只在左上角填充文本
                                                if r == start_row and c == start_col:
                                                    grid[r][c] = text
                                                else:
                                                    # 其他位置填充占位符
                                                    grid[r][c] = '^'  # 使用^作为合并单元格标记
                                
                                # 构建markdown表格
                                markdown_table = []
                                
                                # 检查是否有有效内容
                                has_content = False
                                for row in grid:
                                    if any(cell.strip() for cell in row if cell != '^'):
                                        has_content = True
                                        break
                                
                                if has_content:
                                    # 处理表格，考虑合并单元格
                                    for r in range(rows_count):
                                        row = grid[r]
                                        # 替换合并单元格标记
                                        processed_row = []
                                        for c, cell in enumerate(row):
                                            if cell == '^':
                                                # 检查是横向合并还是纵向合并
                                                if c > 0 and row[c-1] != '^':
                                                    # 横向合并，使用空值
                                                    processed_row.append('')
                                                else:
                                                    # 纵向合并，使用上一行的值
                                                    if r > 0 and grid[r-1][c] != '^':
                                                        processed_row.append(grid[r-1][c])
                                                    else:
                                                        processed_row.append('')
                                            else:
                                                processed_row.append(cell)
                                        
                                        # 添加表格行
                                        markdown_table.append("| " + " | ".join(processed_row) + " |")
                                        
                                        # 添加表头分隔符（在第一行之后）
                                        if r == 0:
                                            markdown_table.append("| " + " | ".join(["---" for _ in range(cols_count)]) + " |")
                                    
                                    # 添加到结果
                                    markdown_tables.append(table_title + "\n".join(markdown_table) + "\n\n")
                                else:
                                    # 表格无有效内容
                                    markdown_tables.append(table_title + "（表格结构存在但无有效内容）\n\n")
                            except Exception as e:
                                logger.error(f"处理表格单元格时出错: {str(e)}", exc_info=True)
                                markdown_tables.append(table_title + f"（处理表格时出错: {str(e)}）\n\n")
                        elif 'cells' in table and table['cells']:
                            # 兼容旧格式表格
                            try:
                                # 确定表格的行数和列数
                                max_row = max([cell.get('row', 0) for cell in table['cells']])
                                max_col = max([cell.get('col', 0) for cell in table['cells']])
                                
                                # 创建空表格
                                rows = [["" for _ in range(max_col+1)] for _ in range(max_row+1)]
                                
                                # 填充表格内容
                                for cell in table['cells']:
                                    row_index = cell.get('row', 0)
                                    col_index = cell.get('col', 0)
                                    text = cell.get('text', '').replace('|', '\\|')  # 转义管道符
                                    rows[row_index][col_index] = text
                                
                                # 构建markdown表格
                                markdown_table = []
                                
                                # 检查是否有有效内容
                                has_content = False
                                for row in rows:
                                    if any(cell.strip() for cell in row):
                                        has_content = True
                                        break
                                
                                if has_content:
                                    # 添加表头
                                    markdown_table.append("| " + " | ".join(rows[0]) + " |")
                                    # 添加表头分隔符
                                    markdown_table.append("| " + " | ".join(["---" for _ in range(max_col+1)]) + " |")
                                    # 添加表格内容
                                    for row in rows[1:]:
                                        markdown_table.append("| " + " | ".join(row) + " |")
                                    
                                    # 添加到结果
                                    markdown_tables.append(table_title + "\n".join(markdown_table) + "\n\n")
                                else:
                                    # 表格无有效内容
                                    markdown_tables.append(table_title + "（表格结构存在但无有效内容）\n\n")
                            except Exception as e:
                                logger.error(f"处理表格单元格时出错: {str(e)}")
                                markdown_tables.append(table_title + f"（处理表格时出错: {str(e)}）\n\n")
                        elif 'position' in table:
                            # 表格有边界但没有单元格，尝试从页面中提取这个区域的文本
                            markdown_tables.append(table_title + "（检测到表格边界但无法提取单元格内容）\n\n")
                        else:
                            # 表格没有单元格数据
                            markdown_tables.append(table_title + "（表格无有效内容）\n\n")
                
                # 处理文本块区域
                if 'blocks' in page:
                    for block_index, block in enumerate(page['blocks']):
                        if block.get('type') != 'table' and 'lines' in block and block['lines']:
                            # 提取文本内容
                            text_lines = []
                            for line in block['lines']:
                                if line.get('text', '').strip():
                                    text_lines.append(line.get('text', ''))
                                    line_count += 1
                            
                            text_content = "\n".join(text_lines)
                            if text_content.strip():
                                markdown_tables.append(f"### 第{page_index+1}页 文本块{block_index+1}\n\n{text_content}\n\n")
                
                # 直接处理页面中的文本行（如果没有blocks和tables或结构不一致）
                if 'lines' in page and (not page.get('tables') and not page.get('blocks')):
                    text_lines = []
                    for line in page['lines']:
                        if line.get('text', '').strip():
                            text_lines.append(line.get('text', ''))
                            line_count += 1
                    
                    text_content = "\n".join(text_lines)
                    if text_content.strip():
                        markdown_tables.append(f"### 第{page_index+1}页 文本内容\n\n{text_content}\n\n")
            
            # 记录处理结果统计
            logger.info(f"表格处理完成: 共处理{table_count}个表格，{line_count}行文本")
            
            if not markdown_tables:
                # 尝试从页面中提取任何可能的文本
                all_text = []
                for page in pages:
                    # 尝试不同的可能结构获取文本
                    if 'lines' in page:
                        page_text = "\n".join([line.get('text', '') for line in page.get('lines', []) if line.get('text', '').strip()])
                        if page_text.strip():
                            all_text.append(page_text.strip())
                    
                    # 尝试从blocks中提取
                    if 'blocks' in page:
                        for block in page['blocks']:
                            if 'lines' in block:
                                block_text = "\n".join([line.get('text', '') for line in block.get('lines', []) if line.get('text', '').strip()])
                                if block_text.strip():
                                    all_text.append(block_text.strip())
                    
                    # 尝试从tables中提取plain表格的文本
                    if 'tables' in page:
                        for table in page['tables']:
                            if table.get('type') == 'plain' and 'lines' in table:
                                table_text = "\n".join([line.get('text', '') for line in table.get('lines', []) if line.get('text', '').strip()])
                                if table_text.strip():
                                    all_text.append(table_text.strip())
                
                if all_text:
                    return "\n\n".join(all_text)
                else:
                    return "未在图片中检测到有效的表格或文本内容。请尝试使用不同的图片或调整图片清晰度。"
            
            return "\n".join(markdown_tables)
            
        except Exception as e:
            logger.error(f"表格转Markdown失败: {str(e)}", exc_info=True)
            return f"表格转换出错: {str(e)}。请尝试使用不同的图片或联系管理员。"

    def process_excel_data(self, result: Dict) -> str:
        """
        处理Excel数据（如果设置了excel=1）

        Args:
            result: 表格识别结果

        Returns:
            str: Base64编码的Excel数据
        """
        try:
            if 'result' in result and 'excel' in result['result']:
                return result['result']['excel']
            return ""
        except Exception as e:
            logger.error(f"处理Excel数据失败: {str(e)}")
            return ""

